//! Match statement and pattern parsing

use super::super::{parse_expression, Parser};
use crate::ast::expr::{Expression, Literal, Pattern};
use crate::ast::stmt::{MatchArmStmt, MatchStmt};
use crate::error::{CompilerError, Result};
use crate::lexer::token::Token;

/// Parse match statement
pub fn parse_match_statement(parser: &mut Parser) -> Result<MatchStmt> {
    parser.expect(&Token::Match)?;

    // Parse expression (optional for guard-only match)
    let expr = if parser.check(&Token::LeftBrace) {
        None
    } else {
        let expr = parse_expression(parser)?;
        if expr.is_none() {
            return Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Expected expression after 'match'",
            ));
        }
        expr
    };

    parser.expect(&Token::LeftBrace)?;

    let mut arms = Vec::new();

    while !parser.check(&Token::RightBrace) {
        if expr.is_some() {
            // Regular match with expression
            let pattern = parse_pattern(parser)?;
            if pattern.is_none() {
                break;
            }

            // Parse guard
            let guard = if parser.consume(&Token::If)? {
                parse_expression(parser)?
            } else {
                None
            };

            parser.expect(&Token::Arrow)?;

            let body = super::parse_statement(parser)?;
            if let (Some(pattern), Some(body)) = (pattern, body) {
                arms.push(MatchArmStmt {
                    pattern,
                    guard,
                    body,
                    location: parser.current_location(),
                });
            }
        } else {
            // Guard-only match
            let guard = if parser.check(&Token::LeftParen) {
                parser.advance()?;
                let guard_expr = parse_expression(parser)?;
                parser.expect(&Token::RightParen)?;
                guard_expr
            } else if parser.check(&Token::Underscore) {
                // Handle wildcard pattern in guard-only match
                parser.advance()?;
                None // No guard for wildcard
            } else {
                parse_expression(parser)?
            };

            parser.expect(&Token::Arrow)?;

            let body = super::parse_statement(parser)?;
            if let Some(body) = body {
                arms.push(MatchArmStmt {
                    pattern: Pattern::Wildcard,
                    guard,
                    body,
                    location: parser.current_location(),
                });
            }
        }

        // Check if we should continue parsing more arms
        // Continue if we find a comma or if we're at the start of a new line
        if parser.consume(&Token::Comma)? {
            continue;
        }

        // If we're at the end of the match block, break
        if parser.check(&Token::RightBrace) {
            break;
        }

        // If we're at the start of a new line and there's a potential pattern/guard, continue
        // This handles the case where arms are separated by newlines without commas
        if matches!(parser.peek().map(|t| &t.token), Some(Token::Integer(_)))
            || matches!(parser.peek().map(|t| &t.token), Some(Token::Float(_)))
            || matches!(parser.peek().map(|t| &t.token), Some(Token::String(_)))
            || matches!(parser.peek().map(|t| &t.token), Some(Token::Identifier(_)))
            || parser.check(&Token::Underscore)
            || parser.check(&Token::LeftParen)
        {
            continue;
        }

        break;
    }

    parser.expect(&Token::RightBrace)?;

    Ok(MatchStmt {
        expr: expr.unwrap_or_else(|| {
            // For guard-only match, create a dummy expression
            Expression::Literal(Literal::Boolean(true))
        }),
        arms,
        location: parser.current_location(),
    })
}

/// Parse pattern for match statements
pub(in crate::parser) fn parse_pattern(parser: &mut Parser) -> Result<Option<Pattern>> {
    // This is the public interface for parse_pattern_or
    parse_pattern_or(parser)
}

/// Parse pattern with OR support (1|2|3)
pub(in crate::parser) fn parse_pattern_or(parser: &mut Parser) -> Result<Option<Pattern>> {
    let mut patterns = Vec::new();

    if let Some(pattern) = parse_pattern_primary(parser)? {
        patterns.push(pattern);

        // Parse additional patterns separated by |
        while parser.consume(&Token::Pipe)? {
            if let Some(pattern) = parse_pattern_primary(parser)? {
                patterns.push(pattern);
            } else {
                return Err(CompilerError::syntax(
                    parser.current_location().line,
                    parser.current_location().column,
                    "Expected pattern after '|'",
                ));
            }
        }

        // If we have multiple patterns, create an Or pattern
        if patterns.len() > 1 {
            Ok(Some(Pattern::Or(patterns)))
        } else {
            Ok(Some(patterns.into_iter().next().unwrap()))
        }
    } else {
        Ok(None)
    }
}

/// Parse primary pattern (without OR)
pub(in crate::parser) fn parse_pattern_primary(parser: &mut Parser) -> Result<Option<Pattern>> {
    if parser.is_at_end() {
        return Ok(None);
    }

    match parser.peek() {
        Some(token) => match &token.token {
            Token::Integer(n) => {
                let n = *n;
                parser.advance()?;
                Ok(Some(Pattern::Literal(Literal::Integer(n))))
            }
            Token::Float(f) => {
                let f = *f;
                parser.advance()?;
                Ok(Some(Pattern::Literal(Literal::Float(f))))
            }
            Token::String(s) => {
                let s = s.clone();
                parser.advance()?;
                Ok(Some(Pattern::Literal(Literal::String(s))))
            }
            Token::Char(c) => {
                let c = *c;
                parser.advance()?;
                Ok(Some(Pattern::Literal(Literal::Char(c))))
            }
            Token::True => {
                parser.advance()?;
                Ok(Some(Pattern::Literal(Literal::Boolean(true))))
            }
            Token::False => {
                parser.advance()?;
                Ok(Some(Pattern::Literal(Literal::Boolean(false))))
            }
            Token::Identifier(name) => {
                let name = name.clone();
                parser.advance()?;
                Ok(Some(Pattern::Variable(name)))
            }
            Token::Underscore => {
                parser.advance()?;
                Ok(Some(Pattern::Wildcard))
            }
            Token::LeftParen => {
                parser.advance()?;
                let mut patterns = Vec::new();

                if !parser.check(&Token::RightParen) {
                    loop {
                        if let Some(pattern) = parse_pattern_or(parser)? {
                            patterns.push(pattern);
                        }

                        if !parser.consume(&Token::Comma)? {
                            break;
                        }
                    }
                }

                parser.expect(&Token::RightParen)?;
                Ok(Some(Pattern::Tuple(patterns)))
            }
            _ => Ok(None),
        },
        None => Ok(None),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::parser::Parser;

    fn parse_match_stmt(input: &str) -> Result<MatchStmt> {
        let mut parser = Parser::new(input.to_string(), None);
        parser.advance()?;
        parse_match_statement(&mut parser)
    }

    #[test]
    fn test_parse_match_statement() {
        let result = parse_match_stmt("match x { 1 -> println(\"one\"); 2 -> println(\"two\"); }");
        assert!(result.is_ok());
        let stmt = result.unwrap();
        // expr is Expression, not Option
        assert_eq!(stmt.arms.len(), 2);
    }

    #[test]
    fn test_parse_match_statement_with_default() {
        let result =
            parse_match_stmt("match x { 1 -> println(\"one\"); _ -> println(\"other\"); }");
        assert!(result.is_ok());
        let stmt = result.unwrap();
        assert!(stmt
            .arms
            .iter()
            .any(|arm| matches!(arm.pattern, Pattern::Wildcard)));
    }

    #[test]
    fn test_parse_match_statement_with_guard() {
        let result = parse_match_stmt(
            "match x { n if n > 0 -> println(\"positive\"); _ -> println(\"zero or neg\"); }",
        );
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_match_statement_empty() {
        // Match statement requires at least one arm or it will fail parsing
        // Let's test with a valid match statement instead
        let result = parse_match_stmt("match x { _ -> {} }");
        assert!(result.is_ok());
        let stmt = result.unwrap();
        assert_eq!(stmt.arms.len(), 1);
    }
}
